<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="vmirv" id="vmirv"><span data-lake-id="ue860dd7f" id="ue860dd7f">典型回答</span></h1>
  <p data-lake-id="u9b1837bc" id="u9b1837bc"><br></p>
  <p data-lake-id="u595ac109" id="u595ac109"><span data-lake-id="u93cba418" id="u93cba418">List的subList方法并没有创建一个新的List，而是使用了原List的视图，这个视图使用内部类SubList表示。所以，我们不能把subList方法返回的List强制转换成ArrayList等类，因为他们之间没有继承关系。正如阿里巴巴Java编码规范所说：</span></p>
  <p data-lake-id="u70752b3e" id="u70752b3e"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1668847006547-20161ef3-2321-467e-9b6b-f8cfc79e0069.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_61%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u8873b954" id="u8873b954"><span data-lake-id="u8c1301ce" id="u8c1301ce">另外，视图和原List的修改还需要注意几点，尤其是他们之间的相互影响：</span></p>
  <p data-lake-id="uc672d9ff" id="uc672d9ff"><br></p>
  <p data-lake-id="u222a73b8" id="u222a73b8"><span data-lake-id="u39841da9" id="u39841da9">1、对父(sourceList)子(subList)List做的非结构性修改（non-structural changes），都会影响到彼此。</span></p>
  <p data-lake-id="ua50e3ddf" id="ua50e3ddf"><br></p>
  <p data-lake-id="u079ead45" id="u079ead45"><span data-lake-id="u4086dc6c" id="u4086dc6c">2、对子List做结构性修改，操作同样会反映到父List上。</span></p>
  <p data-lake-id="u3ab0d3f6" id="u3ab0d3f6"><br></p>
  <p data-lake-id="u84d522a6" id="u84d522a6"><span data-lake-id="u2c400fb2" id="u2c400fb2">3、对父List做结构性修改，会抛出异常ConcurrentModificationException。</span></p>
  <p data-lake-id="u00648966" id="u00648966"><br></p>
  <h1 data-lake-id="ilcm0" id="ilcm0"><span data-lake-id="u4b3f37b6" id="u4b3f37b6">扩展知识</span></h1>
  <p data-lake-id="u47fa6f5a" id="u47fa6f5a"><br></p>
  <p data-lake-id="ua968a95b" id="ua968a95b"><span data-lake-id="ue81eed9c" id="ue81eed9c">subList是List接口中定义的一个方法，该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素，他的返回值也是一个List。</span></p>
  <p data-lake-id="u42172fe8" id="u42172fe8"><br></p>
  <p data-lake-id="ubf59555d" id="ubf59555d"><span data-lake-id="ud4f7bbd8" id="ud4f7bbd8">如以下代码：</span></p>
  <p data-lake-id="uf95451ab" id="uf95451ab"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    List&lt;String&gt; names = new ArrayList&lt;String&gt;() {{
        add("Hollis");
        add("hollischuang");
        add("H");
    }};

    List subList = names.subList(0, 1);
    System.out.println(subList);
}
</code></pre>
  <p data-lake-id="u666fb4bc" id="u666fb4bc"><br></p>
  <p data-lake-id="uffe64767" id="uffe64767"><span data-lake-id="u8a0f99b8" id="u8a0f99b8">以上代码输出结果为：</span></p>
  <p data-lake-id="ube57adb7" id="ube57adb7"><br></p>
  <pre lang="java"><code>
[Hollis]
</code></pre>
  <p data-lake-id="u0b021b54" id="u0b021b54"><br></p>
  <p data-lake-id="u987b5206" id="u987b5206"><span data-lake-id="ue3918c3d" id="ue3918c3d">如果我们改动下代码，将subList的返回值强转成ArrayList试一下：</span></p>
  <p data-lake-id="u310f146f" id="u310f146f"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    List&lt;String&gt; names = new ArrayList&lt;String&gt;() {{
        add("Hollis");
        add("hollischuang");
        add("H");
    }};

    ArrayList subList = names.subList(0, 1);
    System.out.println(subList);
}
</code></pre>
  <p data-lake-id="u1a4bd736" id="u1a4bd736"><br></p>
  <p data-lake-id="u06fd9a91" id="u06fd9a91"><span data-lake-id="ubd8e79dd" id="ubd8e79dd">以上代码将抛出异常：</span></p>
  <p data-lake-id="u6068e402" id="u6068e402"><br></p>
  <pre lang="java"><code>
java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
</code></pre>
  <p data-lake-id="u2cb30d22" id="u2cb30d22"><br></p>
  <p data-lake-id="uf27eac0b" id="uf27eac0b"><span data-lake-id="ua43548d4" id="ua43548d4">不只是强转成ArrayList会报错，强转成LinkedList、Vector等List的实现类同样也都会报错。</span></p>
  <p data-lake-id="uaf87862d" id="uaf87862d"><br></p>
  <p data-lake-id="u546b1167" id="u546b1167"><span data-lake-id="uce5eff26" id="uce5eff26">那么，为什么会发生这样的报错呢？我们接下来深入分析一下。</span></p>
  <p data-lake-id="uae77540d" id="uae77540d"><br></p>
  <h2 data-lake-id="G5OgR" id="G5OgR"><span data-lake-id="u61873812" id="u61873812">底层原理</span></h2>
  <p data-lake-id="u63f5e4ec" id="u63f5e4ec"><br></p>
  <p data-lake-id="uc3208992" id="uc3208992"><span data-lake-id="u31d20e04" id="u31d20e04">首先，我们看下subList方法给我们返回的List到底是个什么东西，这一点在JDK源码中注释是这样说的：</span></p>
  <p data-lake-id="u78f365fc" id="u78f365fc"><br></p>
  <blockquote data-lake-id="u75ede0bc" id="u75ede0bc">
   <p data-lake-id="u870a6785" id="u870a6785"><span data-lake-id="uce495869" id="uce495869">Returns a view of the portion of this list between the specifiedfromIndex, inclusive, and toIndex, exclusive.</span></p>
  </blockquote>
  <p data-lake-id="u890ea71e" id="u890ea71e"><br></p>
  <p data-lake-id="u8fe91a90" id="u8fe91a90"><span data-lake-id="uc4a95521" id="uc4a95521">也就是说subList 返回是一个视图，那么什么叫做视图呢？</span></p>
  <p data-lake-id="u5f6eccae" id="u5f6eccae"><br></p>
  <p data-lake-id="uc293735d" id="uc293735d"><span data-lake-id="u2c675526" id="u2c675526">我们看下subList的源码：</span></p>
  <p data-lake-id="u8f481279" id="u8f481279"><br></p>
  <pre lang="java"><code>
public List&lt;E&gt; subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
</code></pre>
  <p data-lake-id="u57deacf9" id="u57deacf9"><br></p>
  <p data-lake-id="ue89084a6" id="ue89084a6"><span data-lake-id="ue60497f4" id="ue60497f4">这个方法返回了一个SubList，这个类是ArrayList中的一个内部类。</span></p>
  <p data-lake-id="uff2c4410" id="uff2c4410"><br></p>
  <p data-lake-id="u60755741" id="u60755741"><span data-lake-id="u5a1ed54b" id="u5a1ed54b">SubList这个类中单独定义了set、get、size、add、remove等方法。</span></p>
  <p data-lake-id="u3240d7da" id="u3240d7da"><br></p>
  <p data-lake-id="ucf442b9f" id="ucf442b9f"><span data-lake-id="u73fab01e" id="u73fab01e">当我们调用subList方法的时候，会通过调用SubList的构造函数创建一个SubList，那么看下这个构造函数做了哪些事情：</span></p>
  <p data-lake-id="u367c1084" id="u367c1084"><br></p>
  <pre lang="java"><code>
SubList(AbstractList&lt;E&gt; parent,
        int offset, int fromIndex, int toIndex) {
    this.parent = parent;
    this.parentOffset = fromIndex;
    this.offset = offset + fromIndex;
    this.size = toIndex - fromIndex;
    this.modCount = ArrayList.this.modCount;
}
</code></pre>
  <p data-lake-id="u31f2309a" id="u31f2309a"><br></p>
  <p data-lake-id="u84e3b5ec" id="u84e3b5ec"><span data-lake-id="ub59bfd38" id="ub59bfd38">可以看到，这个构造函数中把原来的List以及该List中的部分属性直接赋值给自己的一些属性了。</span></p>
  <p data-lake-id="uc365bebe" id="uc365bebe"><br></p>
  <p data-lake-id="uf7573068" id="uf7573068"><span data-lake-id="ue61594b6" id="ue61594b6">也就是说，SubList并没有重新创建一个List，而是直接引用了原有的List（返回了父类的视图），只是指定了一下他要使用的元素的范围而已（从fromIndex（包含），到toIndex（不包含））。</span></p>
  <p data-lake-id="ua11ae74e" id="ua11ae74e"><br></p>
  <p data-lake-id="ue3fa664e" id="ue3fa664e"><span data-lake-id="u2130916a" id="u2130916a">所以，为什么不能将subList方法得到的集合直接转换成ArrayList呢？因为SubList只是ArrayList的内部类，他们之间并没有继承关系，故无法直接进行强制类型转换。</span></p>
  <p data-lake-id="u03e2687e" id="u03e2687e"><br></p>
  <h2 data-lake-id="o99j2" id="o99j2"><span data-lake-id="u265b69c1" id="u265b69c1">视图有什么问题</span></h2>
  <p data-lake-id="u9f49fa92" id="u9f49fa92"><br></p>
  <p data-lake-id="udf4c9d57" id="udf4c9d57"><span data-lake-id="u3464f06b" id="u3464f06b">前面通过查看源码，我们知道，subList()方法并没有重新创建一个ArrayList，而是返回了一个ArrayList的内部类——SubList。</span></p>
  <p data-lake-id="uc84febc7" id="uc84febc7"><br></p>
  <p data-lake-id="u4df526ed" id="u4df526ed"><span data-lake-id="u05413723" id="u05413723">这个SubList是ArrayList的一个视图。</span></p>
  <p data-lake-id="ua5511c43" id="ua5511c43"><br></p>
  <p data-lake-id="u11f37cde" id="u11f37cde"><span data-lake-id="ube5f5964" id="ube5f5964">那么，这个视图又会带来什么问题呢？我们需要简单写几段代码看一下。</span></p>
  <p data-lake-id="u05ba83b3" id="u05ba83b3"><br></p>
  <p data-lake-id="u20b65e8c" id="u20b65e8c"><strong><span data-lake-id="u56e9d586" id="u56e9d586">1、非结构性改变SubList</span></strong></p>
  <p data-lake-id="ud119f6a6" id="ud119f6a6"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    List&lt;String&gt; sourceList = new ArrayList&lt;String&gt;() {{
        add("H");
        add("O");
        add("L");
        add("L");
        add("I");
        add("S");
    }};

    List subList = sourceList.subList(2, 5);

    System.out.println("sourceList ： " + sourceList);
    System.out.println("sourceList.subList(2, 5) 得到List ：");
    System.out.println("subList ： " + subList);

    subList.set(1, "666");

    System.out.println("subList.set(1,666) 得到List ：");
    System.out.println("subList ： " + subList);
    System.out.println("sourceList ： " + sourceList);

}
</code></pre>
  <p data-lake-id="u9a2d94bd" id="u9a2d94bd"><br></p>
  <p data-lake-id="u087ee9de" id="u087ee9de"><span data-lake-id="u7c081690" id="u7c081690">得到结果：</span></p>
  <p data-lake-id="uf8ec1c56" id="uf8ec1c56"><br></p>
  <pre lang="java"><code>
sourceList ： [H, O, L, L, I, S]
sourceList.subList(2, 5) 得到List ：
subList ： [L, L, I]
subList.set(1,666) 得到List ：
subList ： [L, 666, I]
sourceList ： [H, O, L, 666, I, S]
</code></pre>
  <p data-lake-id="ub39118b0" id="ub39118b0"><br></p>
  <p data-lake-id="u97ee5da2" id="u97ee5da2"><span data-lake-id="u4d77d875" id="u4d77d875">当我们尝试通过set方法，改变subList中某个元素的值得时候，我们发现，原来的那个List中对应元素的值也发生了改变。</span></p>
  <p data-lake-id="u5692a5a9" id="u5692a5a9"><br></p>
  <p data-lake-id="ua0ce92ca" id="ua0ce92ca"><span data-lake-id="ud82cba29" id="ud82cba29">同理，如果我们使用同样的方法，对sourceList中的某个元素进行修改，那么subList中对应的值也会发生改变。读者可以自行尝试一下。</span></p>
  <p data-lake-id="u7e6af6f4" id="u7e6af6f4"><br></p>
  <p data-lake-id="udca7e554" id="udca7e554"><strong><span data-lake-id="u5e7c362d" id="u5e7c362d">1、结构性改变SubList</span></strong></p>
  <p data-lake-id="u9f7ff542" id="u9f7ff542"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    List&lt;String&gt; sourceList = new ArrayList&lt;String&gt;() {{
        add("H");
        add("O");
        add("L");
        add("L");
        add("I");
        add("S");
    }};

    List subList = sourceList.subList(2, 5);

    System.out.println("sourceList ： " + sourceList);
    System.out.println("sourceList.subList(2, 5) 得到List ：");
    System.out.println("subList ： " + subList);

    subList.add("666");

    System.out.println("subList.add(666) 得到List ：");
    System.out.println("subList ： " + subList);
    System.out.println("sourceList ： " + sourceList);

}
</code></pre>
  <p data-lake-id="uba0536c5" id="uba0536c5"><br></p>
  <p data-lake-id="ue775e191" id="ue775e191"><span data-lake-id="u745fc08c" id="u745fc08c">得到结果：</span></p>
  <p data-lake-id="u45ebcbc2" id="u45ebcbc2"><br></p>
  <pre lang="java"><code>
sourceList ： [H, O, L, L, I, S]
sourceList.subList(2, 5) 得到List ：
subList ： [L, L, I]
subList.add(666) 得到List ：
subList ： [L, L, I, 666]
sourceList ： [H, O, L, L, I, 666, S]
</code></pre>
  <p data-lake-id="u2be90db6" id="u2be90db6"><br></p>
  <p data-lake-id="u8778fd1f" id="u8778fd1f"><span data-lake-id="u63fccdfa" id="u63fccdfa">我们尝试对subList的结构进行改变，即向其追加元素，那么得到的结果是sourceList的结构也同样发生了改变。</span></p>
  <p data-lake-id="ube5ccb7e" id="ube5ccb7e"><br></p>
  <p data-lake-id="ub183756a" id="ub183756a"><strong><span data-lake-id="u4fbb7bba" id="u4fbb7bba">1、结构性改变原List</span></strong></p>
  <p data-lake-id="u45986bc8" id="u45986bc8"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    List&lt;String&gt; sourceList = new ArrayList&lt;String&gt;() {{
        add("H");
        add("O");
        add("L");
        add("L");
        add("I");
        add("S");
    }};

    List subList = sourceList.subList(2, 5);

    System.out.println("sourceList ： " + sourceList);
    System.out.println("sourceList.subList(2, 5) 得到List ：");
    System.out.println("subList ： " + subList);

    sourceList.add("666");

    System.out.println("sourceList.add(666) 得到List ：");
    System.out.println("sourceList ： " + sourceList);
    System.out.println("subList ： " + subList);

}
</code></pre>
  <p data-lake-id="u12185d69" id="u12185d69"><br></p>
  <p data-lake-id="u85282720" id="u85282720"><span data-lake-id="u2875065f" id="u2875065f">得到结果：</span></p>
  <p data-lake-id="u9e5bcb94" id="u9e5bcb94"><br></p>
  <pre lang="java"><code>
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
    at java.util.AbstractCollection.toString(AbstractCollection.java:454)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.hollis.SubListTest.main(SubListTest.java:28)
</code></pre>
  <p data-lake-id="ua4151e7c" id="ua4151e7c"><br></p>
  <p data-lake-id="u8636b4c7" id="u8636b4c7"><span data-lake-id="u3233413d" id="u3233413d">我们尝试对sourceList的结构进行改变，即向其追加元素，结果发现抛出了ConcurrentModificationException。</span></p>
  <p data-lake-id="ub202e481" id="ub202e481"><span data-lake-id="u8bf8b912" id="u8bf8b912">​</span><br></p>
  <h2 data-lake-id="AyABp" id="AyABp"><span data-lake-id="ua9cfa0c0" id="ua9cfa0c0">如何创建新的List</span></h2>
  <p data-lake-id="u684d0214" id="u684d0214"><br></p>
  <p data-lake-id="ucd0da658" id="ucd0da658"><span data-lake-id="u782d0e69" id="u782d0e69">如果需要对subList作出修改，又不想动原list。那么可以创建subList的一个拷贝：</span></p>
  <p data-lake-id="u1eb0c366" id="u1eb0c366"><br></p>
  <pre lang="java"><code>
subList = Lists.newArrayList(subList);
list.stream().skip(strart).limit(end).collect(Collectors.toList());
</code></pre>
  <p data-lake-id="ue690b9ad" id="ue690b9ad"><br></p>
  <p data-lake-id="u7695f3e0" id="u7695f3e0"><br></p>
  <p data-lake-id="u8ece7eb3" id="u8ece7eb3"><br></p>
 </body>
</html>